package ar.com.solidussource.howmuchservices.web.controller;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import ar.com.solidussource.framework.backend.bo.exception.BusinessException;
import ar.com.solidussource.framework.backend.dao.filter.Page;
import ar.com.solidussource.framework.backend.dao.filter.Result;
import ar.com.solidussource.framework.backend.dao.filter.TableOrder;
import ar.com.solidussource.howmuchservices.backend.bo.LocationCRUDBO;
import ar.com.solidussource.howmuchservices.backend.bo.MarketBO;
import ar.com.solidussource.howmuchservices.backend.bo.MarketCRUDBO;
import ar.com.solidussource.howmuchservices.backend.bo.MarketcategoryCRUDBO;
import ar.com.solidussource.howmuchservices.backend.bo.ProductpriceCRUDBO;
import ar.com.solidussource.howmuchservices.common.entities.Address;
import ar.com.solidussource.howmuchservices.common.entities.Geopoint;
import ar.com.solidussource.howmuchservices.common.entities.Location;
import ar.com.solidussource.howmuchservices.common.entities.Market;
import ar.com.solidussource.howmuchservices.common.entities.Marketcategory;
import ar.com.solidussource.howmuchservices.common.entities.Productprice;
import ar.com.solidussource.howmuchservices.common.entities.filter.MarketFilter;
import ar.com.solidussource.howmuchservices.common.entities.filter.ProductpriceFilter;
import ar.com.solidussource.howmuchservices.utils.json.JSONUtils;

@Controller
@RequestMapping("/market")
public class MarketController extends BaseController {
	private static final Logger log = Logger.getLogger(MarketController.class);
	private static final String TABLEORDER_CRITERION = "name";
	private static final String TABLEORDER_DIRECTION = "desc";
	private static final String TABLEORDER_PRODUCTPRICE_CRITERION = "score";
	private static final String TABLEORDER_PRODUCTPRICE_DIRECTION = "desc";
	
	@Autowired MarketBO marketBO;
	@Autowired MarketCRUDBO marketCRUDBO;
	@Autowired MarketcategoryCRUDBO marketcategoryCRUDBO;
	@Autowired LocationCRUDBO locationCRUDBO;
	@Autowired ProductpriceCRUDBO productpriceCRUDBO;
		
	@RequestMapping(value="show.json", method = RequestMethod.GET, produces="application/json")
	@ResponseBody
	public String getMarket(HttpServletResponse httpServletResponse, @RequestParam Integer marketId) {
		try {
			Market market = marketCRUDBO.getById(marketId);
			
			return JSONUtils.getJSON(market);
			
		} catch (Exception e) {
			httpServletResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
			log.error(e.getMessage(), e);			
			return getJsonStatus(e.getMessage());
		}	
 
	}
	
	@RequestMapping(value="/list.json", method = RequestMethod.GET, produces="application/json")
	@ResponseBody
	public String getMarkets(HttpServletResponse httpServletResponse, @RequestParam Integer pageNumber, @RequestParam Integer size) {
		
		try {
			MarketFilter marketFilter = new MarketFilter();
			marketFilter.setTableOrder(new TableOrder(TABLEORDER_CRITERION, TABLEORDER_DIRECTION));
			
			Page page = new Page(pageNumber, size);
			
			Result<Market> markets = marketCRUDBO.getAll(marketFilter, page);
			
			return JSONUtils.getJSON(markets.getResult());
			
		} catch (Exception e) {
			httpServletResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
			log.error(e.getMessage(), e);			
			return getJsonStatus(e.getMessage());
		}
 
	}
	
	@RequestMapping(value="/listByProductsAndLocation.json", method = RequestMethod.GET, produces="application/json")
	@ResponseBody
	public String getMarketsByProductsAndLocation(HttpServletResponse httpServletResponse, 
			@RequestParam Integer[] productId, 
			@RequestParam Double locLatitude, 
			@RequestParam Double locLongitude, 
			@RequestParam Integer ratio, 
			@RequestParam Integer pageNumber, 
			@RequestParam Integer size) {
		
		try {
			ProductpriceFilter productpriceFilter = new ProductpriceFilter();
			productpriceFilter.setProductIds(productId);
			productpriceFilter.setTableOrder(new TableOrder(TABLEORDER_PRODUCTPRICE_CRITERION, TABLEORDER_PRODUCTPRICE_DIRECTION));
			
			Page page = new Page(pageNumber, size);
			
			Result<Productprice> result = productpriceCRUDBO.getAll(productpriceFilter, page);
			
			List<Market> markets = new ArrayList<Market>();
			
			Iterator<Productprice> iterator = result.getResult().iterator();
			
			while (iterator.hasNext()) {
				Productprice productprice = iterator.next();				
				Market market = productprice.getMarket();
				
				if (!markets.contains(market) && distanceKm(locLatitude, locLongitude, market.getAddress().getGeopoint().getLatitude(), market.getAddress().getGeopoint().getLongitude()) < ratio) {
					markets.add(market);
				}				
			}
			
			return JSONUtils.getJSON(markets);
			
		} catch (Exception e) {
			httpServletResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
			log.error(e.getMessage(), e);			
			return getJsonStatus(e.getMessage());
		}
 
	}
	
	private double distanceKm(double lat1, double lon1, double lat2, double lon2) {
	    int EARTH_RADIUS_KM = 6371;
	    double lat1Rad = Math.toRadians(lat1);
	    double lat2Rad = Math.toRadians(lat2);
	    double deltaLonRad = Math.toRadians(lon2 - lon1);

	    return Math.acos(Math.sin(lat1Rad) * Math.sin(lat2Rad) + Math.cos(lat1Rad) * Math.cos(lat2Rad) * Math.cos(deltaLonRad)) * EARTH_RADIUS_KM;
	}
	
	@RequestMapping(value="/create.json", method = RequestMethod.PUT, produces="application/json")
	@ResponseBody
	public String createMarket(HttpServletResponse httpServletResponse,
								@RequestParam Integer marketcategoryId, 
								@RequestParam String name, 
								@RequestParam(required = false) String description, 
								@RequestParam(required = false) String phone,
								@RequestParam(required = false) String email,
								@RequestParam(required = false) String businesshour,
								@RequestParam(required = false) Boolean twentyfourhourservice,
								@RequestParam Integer addressLocationId,
								@RequestParam String addressName,
								@RequestParam(required = false) String addressDescription,
								@RequestParam String addressZipcode,
								@RequestParam Double geopointLatitude,
								@RequestParam Double geopointLongitude) {
		
		try {
			Marketcategory marketcategory = marketcategoryCRUDBO.getById(marketcategoryId);
			
			if (marketcategory == null) {
				throw new BusinessException("no marketcategory "+marketcategoryId);
			}
			
			Location location = locationCRUDBO.getById(addressLocationId);
			
			if (location == null) {
				throw new BusinessException("no addressLocation "+addressLocationId);
			}
			
			Geopoint geopoint = new Geopoint();
			geopoint.setLatitude(geopointLatitude);
			geopoint.setLongitude(geopointLongitude);
			
			Address address = new Address();
			address.setLocation(location);
			address.setGeopoint(geopoint);
			address.setName(addressName);
			address.setDescription(addressDescription);
			address.setZipcode(addressZipcode);

			Market market = new Market();
					
			market.setMarketcategory(marketcategory);
			market.setAddress(address);
					
			market.setName(name);
			market.setDescription(description);
			market.setPhone(phone);
			market.setEmail(email);
			market.setBusinesshour(businesshour);
			market.setTwentyfourhourservice(twentyfourhourservice);
					
			market.setScore(0);
			market.setDeleted(false);
					
			market.setCreateuser(getCurrentUser().getId());
			market.setCreatedate(new Date());
					
			marketBO.createMarketFromScratch(market);
					
			return getJsonStatus(RESPONSE_OK);
			
		} catch (Exception e) {
			httpServletResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
			log.error(e.getMessage(), e);			
			return getJsonStatus(e.getMessage());
		}
 
	}
	
	
	@RequestMapping(value="/update.json", method = RequestMethod.PUT, produces="application/json")
	@ResponseBody
	public String updateMarket(HttpServletResponse httpServletResponse,
								@RequestParam Integer marketId,
								@RequestParam Integer marketcategoryId, 
								@RequestParam String name, 
								@RequestParam(required = false) String description, 
								@RequestParam(required = false) String phone,
								@RequestParam(required = false) String email,
								@RequestParam(required = false) String businesshour,
								@RequestParam(required = false) Boolean twentyfourhourservice,
								@RequestParam Integer addressLocationId,
								@RequestParam String addressName,
								@RequestParam(required = false) String addressDescription,
								@RequestParam String addressZipcode,
								@RequestParam Double geopointLatitude,
								@RequestParam Double geopointLongitude) {
		
		try {
			Market market = marketCRUDBO.getById(marketId);
			
			if (market == null) {
				throw new BusinessException("no market "+marketId);				
			}
			
			Marketcategory marketcategory = marketcategoryCRUDBO.getById(marketcategoryId);
			
			if (marketcategory == null) {
				throw new BusinessException("no marketcategory "+marketcategoryId);
			}
			
			Location location = locationCRUDBO.getById(addressLocationId);
			
			if (location == null) {
				throw new BusinessException("no addressLocation "+addressLocationId);
			}			
			
			market.getAddress().getGeopoint().setLatitude(geopointLatitude);
			market.getAddress().getGeopoint().setLongitude(geopointLongitude);
						
			market.getAddress().setLocation(location);
			market.getAddress().setName(addressName);
			market.getAddress().setDescription(addressDescription);
			market.getAddress().setZipcode(addressZipcode);
					
			market.setMarketcategory(marketcategory);
					
			market.setName(name);
			market.setDescription(description);
			market.setPhone(phone);
			market.setEmail(email);
			market.setBusinesshour(businesshour);
			market.setTwentyfourhourservice(twentyfourhourservice);
					
			market.setUpdateuser(getCurrentUser().getId());
			market.setUpdatedate(new Date());
					
			marketBO.updateMarket(market);
					
			return getJsonStatus(RESPONSE_OK);
			
		} catch (Exception e) {
			httpServletResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
			log.error(e.getMessage(), e);			
			return getJsonStatus(e.getMessage());
		}
 
	}
	
	@RequestMapping(value="/score.json", method = RequestMethod.PUT, produces="application/json")
	@ResponseBody
	public String score(HttpServletResponse httpServletResponse, @RequestParam Integer marketId){
		
		try {
			Market market = marketCRUDBO.getById(marketId);
			
			if (market != null) {
				market.setScore(market.getScore() + 1);
				marketCRUDBO.saveOrUpdate(market);
				return getJsonStatus(RESPONSE_OK);	
				
			} else {
				throw new BusinessException("no market "+marketId);
			}
		} catch (Exception e) {
			httpServletResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
			log.error(e.getMessage(), e);			
			return getJsonStatus(e.getMessage());
		}		
	}
	
	@RequestMapping(value="/unscore.json", method = RequestMethod.PUT, produces="application/json")
	@ResponseBody
	public String unscore(HttpServletResponse httpServletResponse, @RequestParam Integer marketId){
		
		try {
			Market market = marketCRUDBO.getById(marketId);
			
			if (market != null) {
				market.setScore(market.getScore() - 1);
				marketCRUDBO.saveOrUpdate(market);
				return getJsonStatus(RESPONSE_OK);	
				
			} else {
				throw new BusinessException("no market "+marketId);
			}
		} catch (Exception e) {
			httpServletResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
			log.error(e.getMessage(), e);			
			return getJsonStatus(e.getMessage());
		}		
	}

}
